require "digest/sha1"

class User
  include Mongoid::Document
  include Mongoid::Timestamps

  field :username, type: String
  field :email, type: String
  field :hashed_password, type: String
  field :salt, type: String
  field :last_login_at, type: Time

  attr_accessor :password, :password_confirmation
  attr_accessor :last_login_at
  attr_protected :hashed_password, :salt

  belongs_to :profile
  embeds_one :settings

  has_and_belongs_to_many :followers, :class_name => "User", :inverse_of => :followings, index: true
  has_and_belongs_to_many :followings, :class_name => "User", :inverse_of => :followers, index: true
  has_many :posts

  index({ username: 1 }, { unique: true })
  index({ email: 1 }, { unique: true })

  accepts_nested_attributes_for :profile
  accepts_nested_attributes_for :settings

  USERNAME_LENGTH_MIN = 4
  PASSWORD_LENGTH_MIN = 8

  USERNAME_LENGTH_MAX = 16
  EMAIL_LENGTH_MAX = 60
  PASSWORD_LENGTH_MAX = 16

  validates_presence_of :username, :email, :password
  validates_uniqueness_of :username, :if => Proc.new { |user| user.username_changed? && user.username.present? }, :case_sensitive => false
  validates_uniqueness_of :email, :if => Proc.new { |user| user.email_changed? && user.email.present? }, :case_sensitive => false
  validates_format_of :username, :with => /^[a-z0-9_\-@\.]*$/i
  validates_length_of :username, minimum: USERNAME_LENGTH_MIN, maximum: USERNAME_LENGTH_MAX, :allow_blank => true
  validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_blank => true
  validates_length_of :email, :maximum => EMAIL_LENGTH_MAX, :allow_nil => true
  validates_confirmation_of :password, :allow_nil => true
  validates_length_of :password, minimum: PASSWORD_LENGTH_MIN, maximum: PASSWORD_LENGTH_MAX, :allow_blank => true
  validates_associated :profile

  before_save :update_hashed_password

  scope :search, lambda {|param|
    where("#{User.table_name}.username like ? or #{User.table_name}.email like ?)", "%#{param}%", "%#{param}%")
  }

  def to_param
    username
  end

  def update_hashed_password
    if self.password
      salt_password(password)
    end
  end

  def self.hash_password(clear_password)
    Digest::SHA1.hexdigest(clear_password || "")
  end

  def self.generate_salt
    SecureRandom.hex(16)
  end

  def salt_password(clear_password)
    self.salt = User.generate_salt
    self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
  end

  def self.try_to_login(username, password)
    username = username.to_s
    password = password.to_s

    return nil if password.empty?

    user = where(username: username).first
    if user
      return nil unless user.check_password?(password)
    end

    user.update_attribute(:last_login_at, Time.now) if user && !user.new_record?
    user
  rescue => text
    raise text
  end

  def check_password?(clear_password)
    User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
  end

  def self.current=(user)
    @current_user = user
  end

  def self.current
    @current_user
  end
end